package com.ld.admin.websocket.chat;

import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;

import javax.servlet.http.HttpSession;
import javax.websocket.EncodeException;
import javax.websocket.EndpointConfig;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.ld.admin.websocket.chat.model.ChatMessage;
import com.ld.admin.websocket.chat.model.ChatMessageType;
import com.ld.admin.websocket.chat.model.ChatUserModel;
import com.ld.admin.websocket.chat.model.UsersOnlineModel;
import com.ld.admin.websocket.chat.transformer.ChatMessageReceiver;
import com.ld.admin.websocket.chat.transformer.ChatMessageSender;
import com.ld.shieldsb.common.core.util.useragent.UserAgentModel;
import com.ld.shieldsb.common.core.util.useragent.UserAgentUtil;
import com.ld.shieldsb.common.web.model.SysUser;
import com.ld.shieldsb.common.web.util.Web;

import lombok.extern.slf4j.Slf4j;

@ServerEndpoint(value = "/chat/{room}/{userid}", encoders = ChatMessageSender.class, decoders = ChatMessageReceiver.class, configurator = ChatConfigurator.class)
@Slf4j
@Component
public class ChatWSServer {
    // 静态变量，用来记录当前在线连接数。AtomicInteger线程安全。
    private static AtomicInteger onlineCount = new AtomicInteger(0);

    // concurrent包的线程安全集合，用来存放每个客户端用户对象。若要实现服务端与单一客户端通信的话，可以使用Map来存放，其中Key可以为用户标识
    private static final List<ChatUserModel> usersOnline = new CopyOnWriteArrayList<>();
    // 与客户端的连接会话集合，需要通过它来给客户端发送数据
    private static final Set<Session> sessions = new CopyOnWriteArraySet<>();

    /**
     * 建立连接调用的方法
     * 
     * @Title open
     * @author 吕凯
     * @date 2019年2月27日 下午1:52:38
     * @param session
     * @param room
     * @param userId
     *            void
     */
    @OnOpen
    public void open(final Session session, EndpointConfig config, @PathParam("room") final String room,
            @PathParam("userid") final long userId) {
//        SystemUser user = BaseDAO.findById(SystemUser.class, userId); //也可以直接从数据库中查
        HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
        SysUser user = (SysUser) httpSession.getAttribute(Web.Attr.SESSION_USER);
        if (user != null) {
            Map<String, List<String>> headers = WebSocketUtil.getHeaders(config);
            sessions.add(session);
            ChatUserModel chatUser = new ChatUserModel();
            chatUser.setRoom(room);
            chatUser.setId(userId);
            chatUser.setLoginName(user.getLoginCode());
            chatUser.setRealName(user.getUserName());
            chatUser.setIdNo(System.currentTimeMillis() + RandomStringUtils.randomAlphanumeric(10));
            chatUser.setIp(WebSocketUtil.getRemoteAddress(session, headers));
            UserAgentModel userAgentModel = UserAgentUtil.getUserAgent(WebSocketUtil.getHeader(headers, "user-agent"));
            if (userAgentModel != null) {
                String clientInfo = userAgentModel.getBrowserType() == null ? "未知" : userAgentModel.getBrowserType();
                if (userAgentModel.getBrowserVersion() != null) {
                    clientInfo += " " + userAgentModel.getBrowserVersion();
                }
                chatUser.setClient(clientInfo);
            }
            usersOnline.add(chatUser); // 加入set中
            onlineCount.set(usersOnline.size()); // addOnlineCount(); // 在线数加1
            log.warn("有新连接加入(" + room + ")！当前在线人数为" + getOnlineCount());
            session.getUserProperties().put("user", chatUser);

            ChatMessage message = new ChatMessage();
            message.setMessage(user.getUserName() + "上线！");
            message.setSender("系统消息");
            message.setSenderName("系统消息");
            message.setReceived(new Date());
            sendNotice(session, room, message);
            // 在线人员返回
            sendUserList(session);
        }
    }

    private void sendUserList(final Session session) {
        try {
            UsersOnlineModel users = new UsersOnlineModel();
            users.getUsers().addAll(usersOnline);
            String userStr = JSON.toJSONString(users, SerializerFeature.BrowserCompatible);
            session.getBasicRemote().sendText(userStr);
        } catch (Exception e) {
            log.error("返回在线人员出错！", e);
        }
    }

    private void sendNotice(final Session session, final String room, ChatMessage message) {
        for (Session s : sessions) {
//        for (Session s : session.getOpenSessions()) { //此方法无法获取所有有效的连接
            if (s.isOpen()) {
                sendMessage(room, message, s);
                sendUserList(s);
            } else {
                closeConn(session);
            }
        }
    }

    private void sendMessage(final String room, ChatMessage message, Session s) {
        if (ChatMessageType.MSGTYPE_USERS_ONLINE.equals(message.getMsgType())) { // 获取在线人员
            sendUserList(s);
        } else {
            if (message != null && !StringUtils.isEmpty(message.getMessage())) {
                ChatUserModel chatUser = (ChatUserModel) s.getUserProperties().get("user");
                if (chatUser != null && room.equals(chatUser.getRoom())) {
                    try {
                        if (StringUtils.isNotBlank(message.getReceiver())) { // 指定了接收人时只发送给自己或接收人
                            if (!message.getReceiver().equals(chatUser.getId() + "")
                                    && !message.getSender().equals(chatUser.getLoginName() + "")) { // 自己或接收人
                                return;
                            }
                        }
                        s.getBasicRemote().sendObject(message); // 不指定接收人时群发
                    } catch (IOException | EncodeException e) {
                        log.error("onMessage failed", e);
                    }
                }
            }

        }
    }

    /**
     * 发送信息
     * 
     * @Title onMessage
     * @author 吕凯
     * @date 2019年2月27日 下午1:52:26
     * @param session
     * @param chatMessage
     *            void
     */
    @OnMessage
    public void onMessage(final Session session, final ChatMessage chatMessage) {
        ChatUserModel chatUser = (ChatUserModel) session.getUserProperties().get("user");
        String room = chatUser.getRoom();
        log.warn(chatMessage.getSendAll() + " " + session.getOpenSessions().size());
//        for (Session s : session.getOpenSessions()) {
        for (Session s : sessions) {
            if (s.isOpen()) {
                sendMessage(room, chatMessage, s);
            } else {
                closeConn(session);
            }
        }
    }

    /**
     * 断开连接调用的方法
     * 
     * @Title onClose
     * @author 吕凯
     * @date 2019年2月27日 下午1:52:51
     * @param session
     *            void
     */
    @OnClose
    public void onClose(Session session) {
        closeConn(session);
        ChatUserModel chatUser = (ChatUserModel) session.getUserProperties().get("user");
        String room = chatUser.getRoom();
        String realName = chatUser.getRealName();
        ChatMessage message = new ChatMessage();
        message.setMessage(realName + "下线！");
        message.setSender("系统消息");
        message.setSenderName("系统消息");
        message.setReceived(new Date());
        sendNotice(session, room, message);
    }

    private void closeConn(Session session) {
        ChatUserModel chatUser = (ChatUserModel) session.getUserProperties().get("user");
        usersOnline.remove(chatUser); // 从set中删除
        sessions.remove(session);
        onlineCount.set(usersOnline.size()); // 重置在线数
        log.warn("有一连接关闭！当前在线人数为" + getOnlineCount());
    }

    /**
     * 发生错误时调用
     * 
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("websocket发生错误!", error);
        closeConn(session);
    }

    public static int getOnlineCount() {
        return onlineCount.get();
    }

    public static void addOnlineCount() {
        onlineCount.incrementAndGet();
    }

    public static void subOnlineCount() {
        onlineCount.decrementAndGet();
    }
}
